package com.otaliastudios.cameraview

import android.content.Context
import android.graphics.*
import android.os.SystemClock
import android.util.AttributeSet
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.otaliastudios.cameraview.frame.MjpegInputStream
import com.otaliastudios.cameraview.livedata.DataRateLiveData
import com.otaliastudios.cameraview.livedata.MarkMapLiveData
import com.otaliastudios.cameraview.livedata.RecordingLiveData
import com.otaliastudios.cameraview.size.Size
import com.otaliastudios.cameraview.video.VideoRecordEngine
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.flow.*
import java.io.*
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.TimeUnit

class MjpegView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : SurfaceView(context, attrs, defStyleAttr),
    SurfaceHolder.Callback,
    LifecycleObserver {
    private var mIn: MjpegInputStream? = null
    var showFps = true
    private var surfaceDone = false
    private lateinit var overlayPaint: Paint
    private var dispWidth = 0
    private var dispHeight = 0
    private var displayMode = 0
    var bmp: Bitmap? = null
    var mLifecycle: Lifecycle? = null
    val warterMap = MarkMapLiveData.get().value
    var fps: String? = null
    private var url: String? = null
    var mjpegLaunch: Job? = null
    var frameLaunch: Job? = null
    var onStreamLiveCallBack: ((rate: Long) -> Unit)? = null
    var onSizeChangedCallback: ((size: Size) -> Unit)? = null
    fun init(url: String?, owner: LifecycleOwner?) {
        this.url = url
        holder.addCallback(this)
        isFocusable = true
        overlayPaint = Paint()
        overlayPaint.textAlign = Paint.Align.LEFT
        overlayPaint.textSize = 18f
        overlayPaint.typeface = Typeface.DEFAULT
        overlayPaint.color = Color.WHITE
        displayMode = SIZE_BEST_FIT
        dispWidth = width
        dispHeight = height
        setLifecycleOwner(owner)
    }


    override fun surfaceChanged(holder: SurfaceHolder, f: Int, w: Int, h: Int) {
        synchronized(holder) {
            dispWidth = width
            dispHeight = height
        }
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        surfaceDone = false
        onPause()
    }


    override fun surfaceCreated(holder: SurfaceHolder) {
        surfaceDone = true
        obtainFrame()
    }


    fun setDisplayMode(s: Int) {
        displayMode = s
    }


    private fun obtainFrame() {
        LogPlus.e("我来获取数据流")
        if (frameLaunch?.isActive == true) return
        frameLaunch = flow {
            val videoUrl = URL(url)
            //利用HttpURLConnection对象从网络中获取网页数据
            val conn = videoUrl.openConnection() as HttpURLConnection
            //设置输入流
            conn.doInput = true
            conn.connectTimeout = 5000
            //连接
            conn.connect()
            //得到网络返回的输入流
            val inputstream = conn.inputStream
            val bis = BufferedInputStream(inputstream)
            mIn = MjpegInputStream(bis)
            while (currentCoroutineContext().isActive) {
                val ret = mIn!!.readFrame()
                emit(ret)
                if (ret == -1) {
                    return@flow
                }
            }
        }.onEach {
            check(it >= 0)
        }.flowOn(IO).retry {
            delay(500)
            true
        }.launchIn(GlobalScope)
    }

    private fun displayMjpeg() {
        var c: Canvas?
        mjpegLaunch = flow {
            var start = System.currentTimeMillis()
            val mode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)
            val p = Paint()
            var frameCounter = 0
            var dataRate = 0L
            LogPlus.e("开启录显示===>${mjpegLaunch?.isActive}")
            while (currentCoroutineContext().isActive) {
                var destRect: Rect? = null
                if (surfaceDone) {
                    val ks = SystemClock.elapsedRealtime()
                    if (mIn == null) delay(500)
                    // val ret = mIn!!.frameToBitmap(bmp)
                    val frame = mIn?.frameQueue?.poll(100, TimeUnit.MILLISECONDS)
                    LogPlus.i("网络获取帧时间===${SystemClock.elapsedRealtime() - ks}")
                    frame?.let {
                        val frist = SystemClock.elapsedRealtime()
                        bmp = BitmapFactory.decodeByteArray(frame, 0, frame.size)
                            .copy(Bitmap.Config.ARGB_8888, true)
                        LogPlus.i("Bitmap==>${SystemClock.elapsedRealtime() - frist}")
                        val ret = 0
                        emit(ret)
                        if (ret == -1) {
                            return@flow
                        }
                        bmp?.let { b ->
                            destRect = destRect(b.width, b.height)
                            val size = Size(destRect!!.width(), destRect!!.height())
                            onSizeChangedCallback?.invoke(size)
                            //  LogPlus.e("${size}===${bmp?.width}===${bmp?.height}")
                            c = holder.lockCanvas()
                            synchronized(holder) {
                                val hs = SystemClock.elapsedRealtime()
                                val canvas = Canvas(b)
                                canvas.let { cav ->
                                    // canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) // 清空画布
                                    warterMap.forEach {
                                        if (it.value.bitmap != null) {
                                            cav.drawBitmap(
                                                it.value.bitmap!!, it.value.point.x.toFloat(),
                                                it.value.point.y.toFloat(), it.value.paint
                                            )
                                        } else {
                                            val split = it.value.text.split("\\r?\\n".toRegex())
                                            for ((i, str) in split.withIndex()) {
                                                cav.drawText(
                                                    str, it.value.point.x.toFloat(),
                                                    it.value.point.y.toFloat() + (i * it.value.textsize),
                                                    it.value.paint
                                                )
                                            }
                                        }
                                    }
                                    if (showFps) cav.drawText(
                                        "$fps", b.width - 50F, 20F, overlayPaint
                                    )
                                }
                                recordEngine?.srcBitmap = b
                                c?.drawBitmap(b, null, destRect!!, p)
                                LogPlus.i("绘制时间===>${SystemClock.elapsedRealtime() - hs}")
                            }
                            frameCounter++
                            dataRate += it.size
                            if (System.currentTimeMillis() - start >= 1000) {
                                fps = frameCounter.toString() + "fps"
                                frameCounter = 0
                                DataRateLiveData.get().postValue(dataRate / 1024)
                                onStreamLiveCallBack?.invoke(dataRate)
                                dataRate = 0L
                                start = System.currentTimeMillis()
                            }

                            if (c != null) holder.unlockCanvasAndPost(c)
                            LogPlus.i(
                                "绘制一帧的时间==>${SystemClock.elapsedRealtime() - frist}==网络和绘制总时间=>${SystemClock.elapsedRealtime() - ks}"
                            )
                        }
                    }
                } else LogPlus.e("SurfaceView 未准备好")
            }
        }.onEach {
            check(it >= 0)
        }.flowOn(IO).retry {
            // obtainBmp()
            LogPlus.e(it.toString())
            true
        }.launchIn(GlobalScope)
    }

    private fun destRect(bmw: Int, bmh: Int): Rect? {
        var bmw = bmw
        var bmh = bmh
        val tempx: Int
        val tempy: Int
        if (displayMode == SIZE_STANDARD) {
            tempx = dispWidth / 2 - bmw / 2
            tempy = dispHeight / 2 - bmh / 2
            return Rect(tempx, tempy, bmw + tempx, bmh + tempy)
        }
        if (displayMode == SIZE_BEST_FIT) {
            val bmasp = bmw.toFloat() / bmh.toFloat()
            bmw = dispWidth
            bmh = (dispWidth / bmasp).toInt()
            if (bmh > dispHeight) {
                bmh = dispHeight
                bmw = (dispHeight * bmasp).toInt()
            }
            tempx = dispWidth / 2 - bmw / 2
            tempy = dispHeight / 2 - bmh / 2
            return Rect(tempx, tempy, bmw + tempx, bmh + tempy)
        }
        return if (displayMode == SIZE_FULLSCREEN) Rect(0, 0, dispWidth, dispHeight) else null
    }


    fun takePhoto(path: String): String? {
        var fos: OutputStream? = null
        try {
            val file = File(path)
            fos = FileOutputStream(file)
            val isSuccess = bmp?.compress(Bitmap.CompressFormat.JPEG, 100, fos)
            LogPlus.e("Bitmap保存图片成功==$isSuccess===>$path")
            if (isSuccess == true) return path
        } catch (e: Exception) {
            e.printStackTrace()
        } catch (error: OutOfMemoryError) {
            error.printStackTrace()
        } finally {
            try {
                fos?.flush()
                fos?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return null
    }


    companion object {
        const val TAG = "MJPEG"
        const val POSITION_UPPER_LEFT = 9
        const val POSITION_UPPER_RIGHT = 3
        const val POSITION_LOWER_LEFT = 12
        const val POSITION_LOWER_RIGHT = 6
        const val SIZE_STANDARD = 1
        const val SIZE_BEST_FIT = 4
        const val SIZE_FULLSCREEN = 8
    }

    private fun setLifecycleOwner(owner: LifecycleOwner?) {
        if (owner == null) {
            clearLifecycleObserver()
        } else {
            clearLifecycleObserver()
            mLifecycle = owner.lifecycle
            mLifecycle?.addObserver(this)
        }
    }

    private fun clearLifecycleObserver() {
        mLifecycle?.removeObserver(this)
        mLifecycle = null

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun onResume() {
        LogPlus.w("我来了=onResume=>开启")
        holder.addCallback(this)
        postDelayed({
            if (mjpegLaunch?.isActive != true) {
                displayMjpeg()
            }
        }, 100)

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun onPause() {
        recordEngine?.stopRecord()
        LogPlus.w("我来了=onPause=>关闭")
        frameLaunch?.cancel()
        mjpegLaunch?.cancel()
        if (mIn != null) {
            try {
                mIn!!.close()
            } catch (e: IOException) {
            }
            mIn = null
        }
    }


    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun freeCameraMemory() {
        mjpegLaunch?.cancel()
        frameLaunch?.cancel()
        mIn?.freeCameraMemory()
    }

    var recordEngine: VideoRecordEngine? = null
    fun record(path: File): Boolean {
        if (recordEngine != null) return false
        if (mjpegLaunch?.isActive == true) {
            bmp?.let {
                recordEngine = VideoRecordEngine(path, 25, it)
                recordEngine?.start()
                RecordingLiveData.get().value = true
                return true
            }
        }
        return false
    }

    fun stopRecord(): String? {
        val path = recordEngine?.stopRecord()
        RecordingLiveData.get().value = false
        recordEngine = null
        return path
    }

}